// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: shared_lib.hpp 318 2009-03-02 14:03:21Z heavyzheng $
//

#ifndef ZZHENG_UTILITY_SHARED_LIB_HPP_20080828__
#define ZZHENG_UTILITY_SHARED_LIB_HPP_20080828__

#include <zzheng/config.hpp>

#if defined(ZZHENG_WIN32)
#  include <zzheng/utility/shared_lib_win32.hpp>
#else
#  include <zzheng/utility/shared_lib_dummy.hpp>
#endif // ZZHENG_WIN32

#include <zzheng/utility/sys_env.hpp>
#include <boost/shared_ptr.hpp>
#include <exception>
#include <string>

namespace zzheng {
namespace sys {

    //! \addtogroup utility zzheng.utility
    //! \{

    //! This class represents an error when a requested function cannot be found.
    class fn_not_found: public std::exception {

    public:

        explicit fn_not_found(): what_() {
            // Do nothing.
        }

        explicit fn_not_found(const char* what): what_(what) {
            // Do nothing.
        }

        virtual ~fn_not_found() throw() {
            // Do nothing.
        }

        // Use auto-generated copy constructor.
        // Use auto-generated copy assignment.

        virtual const char* what() const throw() {
            return what_.c_str();
        }

    private:

        std::string what_;

    }; // class fn_not_found


    ////////////////////////////////////////////////////////////////////////////////////////////////

    //! This class wraps a shared library. It is based on the class detail::shared_lib_wrapper,
    //! whose implementation is platform-dependent. This class requires that the following member
    //! functions be declared and implemented in detail::shared_lib_wrapper:
    //!   - shared_lib_wrapper(const std::string& path, bool auto_free);
    //!   - bool loaded() const;
    //!   - bool failed() const;
    //!   - void* get_fn_addr(const std::string& name);
    class shared_lib {

        typedef detail::shared_lib_wrapper impl_type;

    public:

        //! Constructs a shared_lib object to wrap a shared library. Whether the shared library
        //! will be lazy-loaded is up to the implementation.
        //! \param lib_path   the path to the shared library. System variables will be expanded.
        //! \param auto_free  whether to free the shared library on destruction.
        explicit shared_lib(const std::string& lib_path, bool auto_free = false)
        : pimpl_(), path_(expand_sys_var(lib_path)), auto_free_(auto_free) {
            pimpl_.reset(new detail::shared_lib_wrapper(path_, auto_free_));
        }

        // Use auto-generated copy constructor.
        // Use auto-generated copy assignment.
        // Use auto-generated destructor.

        //! Checks if the shared library will be freed on destruction.
        //! \return true if if the shared library will be freed on destruction, false otherwise.
        bool auto_free() const {
            return auto_free_;
        }

        //! Checks if the shared library has been loaded or not. If the shared library has not been
        //! loaded, it may have never been used (lazy-loading), or it may have failed to be loaded.
        //! \return true if the shared library has been loaded, false otherwise.
        bool loaded() const {
            return impl().loaded();
        }

        //! Checks if the shared library has failed to be loaded.
        //! \return true if the shared library has failed to be loaded, false otherwise.
        bool failed() const {
            return impl().failed();
        }

        //! Returns the path to the shared library.
        //! \return the path to the shared library.
        std::string path() const {
            return path_;
        }

        //! Returns the base name of the shared library.
        //! \return the base name of the shared library.
        std::string basename() const {
            std::string::size_type index = path_.find_last_of("/\\");
            if (index != std::string::npos) {
                if (index + 1 < path_.size()) {
                    return path_.substr(index + 1);
                } else {
                    return std::string();
                }
            } else {
                return path_;
            }
        }

        //! Returns a function pointer by name. If the shared library has not been loaded, this
        //! function will try to load it. If the requested function cannot be found,
        //!   - if the requested function is required, an exception is thrown.
        //!   - if the requested function is not required, null is returned.
        //! \tparam F         the requested function pointer type.
        //! \param  name      the requested function name.
        //! \param  required  if the requested function is required or not.
        //! \return the requested function pointer.
        //! \throws fn_not_found  if the requested function is required but not found.
        template<typename F>
        F get_fn(const std::string& name, bool required = false) {
            void* addr = get_fn_addr(name);
            if (addr != 0) {
                return static_cast<F>(addr);
            } else if (!required) {
                return 0;
            } else {
                std::string errmsg = "Fail to get function '" + name + "': ";
                if (loaded()) {
                    errmsg += "function cannot be found in library [" + path_ + "]";
                } else {
                    errmsg += "library [" + path_ + "] cannot be loaded.";
                }
                throw fn_not_found(errmsg.c_str());
            }
        }

        //! Returns the raw address of the function pointer by name.
        //! \param name  the requested function name.
        //! \return the raw address of the function pointer, or null if not found.
        void* get_fn_addr(const std::string& name) {
            return impl().get_fn_addr(name);
        }

    private:

        const impl_type& impl() const {
            assert(pimpl_ != 0 && "shared_lib_wrapper should not be null.");
            return *pimpl_;
        }

        impl_type& impl() {
            assert(pimpl_ != 0 && "shared_lib_wrapper should not be null.");
            return *pimpl_;
        }

    private:

        boost::shared_ptr<impl_type> pimpl_;     //!< Shared library wrapper.
        std::string                  path_;      //!< Path to the shared library.
        bool                         auto_free_; //!< Whether to free on destruction.

    }; // class shared_lib

    //! \} end group utility

} // namespace zzheng::sys
} // namespace zzheng

#endif // ZZHENG_UTILITY_SHARED_LIB_HPP_20080828__



